Bu yazı dizisinde Flutter ve Clean Architecture arasındaki ilişkiyi inceleyip, kendi projelerimizde nasıl kullanabiliriz bunlardan bahsedeceğim. Ardından bu mimariye bir de Bloc pattern ilave edip, state’lerimizi en optimal şekilde yönetip, kaliteli bir yapı ortaya çıkartmaya çalışacağız.
Öncelikle Clean Architecture nedir bundan bahsedelim. Clean Architecture, Flutter’dan bağımsız olarak kullanılan bir mimari çeşitidir. En büyük avantajlarından birisi katmanlar arasında yüksek derecede soyutluk içerir, haliyle bu da test edilebilirliği, yapılar arasındaki geliştirilebilirliği ve kod ortamındaki düzeni en üst seviyeye çıkartmaktadır.
Bu yazıda genel olarak neler yapacağımızdan ve neden Clean Architecture’ı kullanacağımızdan bahsedeceğim. Ardından bir sonraki yazıda bu mimariyi uygulayıp, en sonda BloC ile birleştirip, yapımızı tamamlayacağız.
Neden Clean Architecture?
Katmanlar arasındaki soyutlama gücü sayesinde her bir katmanın kendi ile ilgili işleri yapmasını sağlamak ve diğer katmanlar ile bağlantısını kesmek amacıyla ortaya çıkan yapı, proje büyüdüğünde de çok rahat bir yönetim sağladığı için tercih ediliyor.
Soğan (Onion) yapısına göre dıştan içe doğru bir bağlılık gerektiren yapımızda bağımlıklar sadece bir katman arasında olmaktadır. Diğer katmanlar birbirinden habersiz bir şekilde işlerine devam edebilmektedir. Bu da test edilebilirliği son derece arttırmaktadır.
Görselde de görüldüğü üzere yapılar arasındaki iletişim bire-birdir. Presentation katmanı Data katmanı ile bir alışveriş içerisinde değildir. Sadece Domain ile bilgi alışverişi içerisinde bulunmaktadır.
Katmanlar arası soyutlama, test yazma işleminin kolaylaşması, büyüyen projelerdeki modülerliğin korunması gibi faydaları Clean Architecture’ı seçmemizdeki büyük etkenler arasında yer alıyor.
Kullanacağımız genel yapının görünümü;
- Data
- Dependency Container
- Repository (Implementation)
- Domain
- Model
- Repository (Interface)
- UseCase
- Presentation
- View
- ViewModel (BloC Pattern)
Şimdi tek tek yapıların görevlerini ve ne işe yaradılarından biraz bahsedelim.
Dependency Container
Uygulamamızda ki repository, networkmanager, bloc gibi yapılardan birer nesne oluşturup, uygulamanın başlangıcında tüm bağımlılıkları atamış olduğumuz yapı. Dependency container kısmında getIt paketinden yararlandım. Neden? İkinci yazıda daha detaylı anlatacağım ama kısaca bahsedeyim. Oluşturulacak nesnelerin Singleton mı yoksa Factory’mi olacağını seçebiliyoruz. Yani uygulamanın yaşam döngüsü boyunca bir defa veya her atandığında yeni bir nesne olup olmayacağını seçmemize izin veriyor. Dolayısıyla işimizi de oldukça kolaylaştırıyor.
Repository (Implementation)
Burada, Repository Interface, kısmında soyut olarak tanımladığımız fonksiyonların işlevlerini veriyoruz. Bir nevi içini dolduruyoruz diyebiliriz. Bu yapıda sınıf içerisine bir NetworkManager alıyoruz ve bu manager sayesinde API tarafına isteklerimizi gönderiyoruz.
Model
Uygulama içerisinde backend tarafına gidecek ve backend tarafından gelecek olan istekleri karşılaması için oluşturduğumuz request ve response modelleri oluşturuyoruz. Bu modeller ile giden/gelen istekleri json formatından, daha modüler bir yapıya dönüştürüyoruz. (Klasik modelleme mantığı)
Repository (Interface)
Bu yapıda ise uygulamadaki işlevlerin, soyut sınıflarını ve fonksiyon tanımlamalarını yapıyoruz. Fonksiyonların isimlendirmesini ve alacağı parametreleri belirleyip, Repository (Implementation) yapısı için bir kural tanımlaması yapıyoruz diyebiliriz.
UseCase
Clean Architecture’da kullanmayı en çok sevdiğim yapı usecase dir. Uygulama içindeki bütünlüğü oldukça kolay ve etkili bir şekilde sağlıyor. Ne işe yarıyor bu Usecase? Aslında direkt olarak Repository-ViewModel arasında bir köprü görevi görüyor ve tek sorumluluk ilkesini uyguluyor. Test edilebilirlik çok daha kolay. Temiz ve anlaşılır kod prensibini arttırıyor. Usecase’lerde ihtiyaç dahilindeki bağımlılıklar yer alır.
View
Kullanıcının ekranda gördüğü her şeyin yer aldığı kısım. UI bileşenlerinin tasarım kurallarına uygun bir şekilde gösterildiği görünüm kısmıdır.
ViewModel
Burada ise UseCase’den aldığımız veriyi işlediğimiz, gerekiyorsa işleme tabii tuttuğumuz, UI bileşenlerinin kontrollerini içeren, kullanıcı ile dinamik bir yapı oluşturmamız sağlayan yapıdır. Biz ViewModel içerisinde BloC pattern uygulayacağız. Burada da güzel bir state yapısı kurup, veri aktarımını efektif bir şekilde yapacağız.
Bu yazıda genel olarak Flutter tarafında kullanacağımız Clean Architecture’dan bahsetmiş olduk. Bir sonraki yazıda ise burada bahsettiğimiz yapıları nasıl oluşturacağımızdan ve nasıl birbirleriyle haberleştireceğimizden bahsedip, kodlama kısmına geçeceğiz.